// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using FluentAssertions;
using Nethermind.Blockchain;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Core.Specs;
using Nethermind.Core.Test.Builders;
using Nethermind.Evm.State;
using Nethermind.Evm.TransactionProcessing;
using Nethermind.Logging;
using Nethermind.Specs.ChainSpecStyle;
using Nethermind.State;
using NSubstitute;
using NUnit.Framework;

namespace Nethermind.Optimism.Test;

public class OptimismGenesisLoaderTests
{
    [Test]
    public void LoadGenesis_ShouldUpdateWithdrawalsRoot_WhenIsthmusIsEnabled()
    {
        // This block matches the one generated by the `optimism-package` Kurtosis setup
        var genesis = Build.A.Block
            .Genesis
            .WithNonce(0)
            .WithDifficulty(0)
            .WithTimestamp(1755537947)
            .WithBaseFeePerGas(1000000000)
            .WithGasUsed(0)
            .WithBlobGasUsed(0)
            .WithGasLimit(60000000)
            .WithParentBeaconBlockRoot(Hash256.Zero)
            .WithAuthor(new Address("0x4200000000000000000000000000000000000011"))
            .WithBeneficiary(new Address("0x4200000000000000000000000000000000000011"))
            .WithRequestsHash(new Hash256("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"))
            .WithStateRoot(new Hash256("0xd977c5f1679de709aef87832d3b3b08009fd1b2df5672021f61aa30b3981c2dd"))
            .WithExtraData(Bytes.FromHexString("0x00000000fa00000006"))
            .TestObject;
        var storageRoot = new ValueHash256("0x8ed4baae3a927be3dea54996b4d5899f8c01e7594bf50b17dc1e741388ce3d12");

        var chainspec = Substitute.For<ChainSpec>();
        chainspec.Genesis = genesis;
        chainspec.Allocations = [];
        chainspec.GenesisStateUnavailable = true;

        var specHelper = Substitute.For<IOptimismSpecHelper>();
        specHelper.IsIsthmus(Arg.Is<BlockHeader>(header => header.Number >= 0)).Returns(true);

        var stateProvider = Substitute.For<IWorldState>();
        stateProvider.TryGetAccount(PreDeploys.L2ToL1MessagePasser, out Arg.Any<AccountStruct>()).Returns(args =>
        {
            args[1] = new AccountStruct(default, default, storageRoot: storageRoot, default);
            return true;
        });

        var specProvider = Substitute.For<ISpecProvider>();
        var transactionProcessor = Substitute.For<ITransactionProcessor>();
        var logManager = Substitute.For<ILogManager>();

        var withdrawalProcessor = new OptimismWithdrawalProcessor(stateProvider, logManager, specHelper);
        var postProcessor = new OptimismGenesisPostProcessor(withdrawalProcessor, specProvider);

        var loader = new GenesisBuilder(
            chainspec,
            specProvider,
            stateProvider,
            transactionProcessor,
            [postProcessor]
        );

        var actualGenesis = loader.Build();

        var genesisHash = actualGenesis.Hash;
        genesisHash.Should().Be(new Hash256("0xab1c80ef327bcd962e1e8569d918702dd7e3888d3483869083528c9e0bde5890"));

        var withdrawalsRoot = actualGenesis.WithdrawalsRoot;
        withdrawalsRoot.Should().Be(new Hash256(storageRoot));
    }
}
